#![allow(clippy::large_enum_variant)]

use serde::{Serialize, Deserialize};

pub mod bar;
pub mod bar3d;
pub mod boxplot;
pub mod candlestick;
pub mod custom;
pub mod effect_scatter;
pub mod funnel;
pub mod gauge;
pub mod graph;
pub mod heatmap;
pub mod line;
pub mod lines;
pub mod map;
pub mod parallel;
pub mod pictorial_bar;
pub mod pie;
pub mod radar;
pub mod sankey;
pub mod scatter;
pub mod sunburst;
pub mod theme_river;
pub mod tree;
pub mod treemap;

pub use bar::*;
pub use bar3d::*;
pub use boxplot::*;
pub use candlestick::*;
pub use custom::*;
pub use effect_scatter::*;
pub use funnel::*;
pub use gauge::*;
pub use graph::*;
pub use heatmap::*;
pub use line::*;
pub use lines::*;
pub use map::*;
pub use parallel::*;
pub use pictorial_bar::*;
pub use pie::*;
pub use radar::*;
pub use sankey::*;
pub use scatter::*;
pub use sunburst::*;
pub use theme_river::*;
pub use tree::*;
pub use treemap::*;

use crate::{datatype::DataFrame, element::MarkLine};

#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub enum Series {
    Bar(bar::Bar),
    Bar3d(bar3d::Bar3d),
    Boxplot(boxplot::Boxplot),
    Candlestick(candlestick::Candlestick),
    Custom(custom::Custom),
    EffectScatter(effect_scatter::EffectScatter),
    Funnel(funnel::Funnel),
    Gauge(gauge::Gauge),
    Graph(graph::Graph),
    Heatmap(heatmap::Heatmap),
    Line(line::Line),
    Map(map::Map),
    Parallel(parallel::Parallel),
    PictorialBar(pictorial_bar::PictorialBar),
    Pie(pie::Pie),
    Radar(radar::Radar),
    Sankey(sankey::Sankey),
    Scatter(scatter::Scatter),
    Sunburst(sunburst::Sunburst),
    ThemeRiver(theme_river::ThemeRiver),
    Tree(tree::Tree),
    Treemap(treemap::Treemap),
}
impl Series {
    pub fn get_name(&self) -> Option<&str> {
        match self {
            Series::Bar(s) => s.get_name(),
            Series::Bar3d(s) => s.get_name(),
            Series::Boxplot(s) => s.get_name(),
            Series::Candlestick(s) => s.get_name(),
            Series::Custom(s) => s.get_name(),
            Series::EffectScatter(s) => s.get_name(),
            Series::Funnel(s) => s.get_name(),
            Series::Gauge(s) => s.get_name(),
            Series::Graph(s) => s.get_name(),
            Series::Heatmap(s) => s.get_name(),
            Series::Line(s) => s.get_name(),
            Series::Map(_s) => None,
            Series::Parallel(_s) => None,
            Series::PictorialBar(s) => s.get_name(),
            Series::Pie(s) => s.get_name(),
            Series::Radar(s) => s.get_name(),
            Series::Sankey(s) => s.get_name(),
            Series::Scatter(s) => s.get_name(),
            Series::Sunburst(s) => s.get_name(),
            Series::ThemeRiver(s) => s.get_name(),
            Series::Tree(s) => s.get_name(),
            Series::Treemap(s) => s.get_name(),
        }
    }
    pub fn set_name(self, name: String) -> Self {
        match self {
            Series::Bar(s) => Series::Bar(s.name(name)),
            Series::Bar3d(s) => Series::Bar3d(s.name(name)),
            Series::Boxplot(s) => Series::Boxplot(s.name(name)),
            Series::Candlestick(s) => Series::Candlestick(s.name(name)),
            Series::Custom(s) => Series::Custom(s.name(name)),
            Series::EffectScatter(s) => Series::EffectScatter(s.name(name)),
            Series::Funnel(s) => Series::Funnel(s.name(name)),
            Series::Gauge(s) => Series::Gauge(s.name(name)),
            Series::Graph(s) => Series::Graph(s.name(name)),
            Series::Heatmap(s) => Series::Heatmap(s.name(name)),
            Series::Line(s) => Series::Line(s.name(name)),
            Series::Map(s) => Series::Map(s),
            Series::Parallel(s) => Series::Parallel(s),
            Series::PictorialBar(s) => Series::PictorialBar(s.name(name)),
            Series::Pie(s) => Series::Pie(s.name(name)),
            Series::Radar(s) => Series::Radar(s.name(name)),
            Series::Sankey(s) => Series::Sankey(s.name(name)),
            Series::Scatter(s) => Series::Scatter(s.name(name)),
            Series::Sunburst(s) => Series::Sunburst(s.name(name)),
            Series::ThemeRiver(s) => Series::ThemeRiver(s.name(name)),
            Series::Tree(s) => Series::Tree(s.name(name)),
            Series::Treemap(s) => Series::Treemap(s.name(name)),
        }
    }
    pub fn set_mark_line(self, mark_line: MarkLine) -> Self {
        match self {
            Series::Line(s) => Series::Line(s.mark_line(mark_line)),
            _ => self,
        }
    }
    pub fn set_data_frame(self,data: DataFrame)->Self {
        match self {
            Series::Bar(s) => Series::Bar(s.data(data)),
            Series::Bar3d(s) => Series::Bar3d(s.data(data)),
            Series::Boxplot(s) => Series::Boxplot(s.data(data)),
            Series::Candlestick(s) => Series::Candlestick(s.data(data)),
            Series::Custom(s) => Series::Custom(s.data(data)),
            Series::EffectScatter(s) => Series::EffectScatter(s.data(data)),
            Series::Funnel(s) => Series::Funnel(s.data(data)),
            Series::Gauge(s) => Series::Gauge(s.data(data)),
            Series::Graph(s) => Series::Graph(s),
            Series::Heatmap(s) => Series::Heatmap(s),
            Series::Line(s) => Series::Line(s.data(data)),
            Series::Map(s) => Series::Map(s),
            Series::Parallel(s) => Series::Parallel(s.data(data)),
            Series::PictorialBar(s) => Series::PictorialBar(s),
            Series::Pie(s) => Series::Pie(s.data(data)),
            Series::Radar(s) => Series::Radar(s.data(data)),
            Series::Sankey(s) => Series::Sankey(s),
            Series::Scatter(s) => Series::Scatter(s.data(data)),
            Series::Sunburst(s) => Series::Sunburst(s),
            Series::ThemeRiver(s) => Series::ThemeRiver(s),
            Series::Tree(s) => Series::Tree(s),
            Series::Treemap(s) => Series::Treemap(s),
        }
    }
}

macro_rules! impl_series {
    ($($variant:ident),*) => {
        impl Serialize for Series {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::ser::Serializer,
            {
                match self {
                    $(Self::$variant(series) => series.serialize(serializer),)*
                }
            }
        }
        impl<'de> Deserialize<'de> for Series {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where
                D: serde::de::Deserializer<'de>,
            {
                let value = serde_json::Value::deserialize(deserializer)?;
                let stype= value.get("type").ok_or_else(|| serde::de::Error::custom("missing series type"))?;
                let series = match stype {
                    $(serde_json::Value::String(string) if *string == stringify!($variant).to_lowercase() => {
                        $variant::deserialize(value)
                            .map(Series::$variant)
                            .map_err(serde::de::Error::custom)?
                    })*
                    _ => return Err(serde::de::Error::custom("invalid series type")),
                };
                Ok(series)
            }
        }
        $(
            impl From<$variant> for Series {
                fn from(series: $variant) -> Self {
                    Self::$variant(series)
                }
            }
        )*
    }
}

impl_series!(
    Bar,
    Bar3d,
    Boxplot,
    Custom,
    Candlestick,
    EffectScatter,
    Funnel,
    Gauge,
    Graph,
    Heatmap,
    Line,
    Map,
    Parallel,
    PictorialBar,
    Pie,
    Radar,
    Sankey,
    Scatter,
    Sunburst,
    ThemeRiver,
    Tree,
    Treemap
);
